﻿// <copyright file="DonorInformation.cs" company="Microsoft Corporation">
// Copyright (c) 2009 Microsoft Corporation All Rights Reserved
// </copyright>
// <author>Michael S. Scherotter</author>
// <email>mischero@microsoft.com</email>
// <date>2009-06-12</date>
// <summary>DonorInformation class definition</summary>

namespace NYTimes
{
    using System;
    using System.Collections.ObjectModel;
    using System.Globalization;
    using System.Linq;
    using System.Text;
    using NYTimes.Core;
    using NYTimes.Data;

    /// <summary>
    /// New York Times Campaign Finance Donor Information API
    /// </summary>
    public class DonorInformation : CampaignFinance
    {
        /// <summary>
        /// the donor's last name
        /// </summary>
        private string lastName;

        /// <summary>
        /// the result set offset
        /// </summary>
        private int offset;

        /// <summary>
        /// the zip code
        /// </summary>
        private string zipCode;

        /// <summary>
        /// the year (currently only 2008) will work
        /// </summary>
        private int year;

        /// <summary>
        /// the first name
        /// </summary>
        private string firstName;

        /// <summary>
        /// the maximum number of results to get
        /// </summary>
        private int maxResults;

        /// <summary>
        /// Initializes a new instance of the DonorInformation class.
        /// </summary>
        public DonorInformation()
        {
            this.Results = new ObservableCollection<Donor>();

            this.MaxResults = int.MaxValue;
        }

        /// <summary>
        /// Gets or sets the donor's last name
        /// </summary>
        public string LastName
        {
            get
            {
                return this.lastName;
            }

            set
            {
                if (this.lastName != value)
                {
                    this.lastName = value;

                    this.Offset = 0;
                }
            }
        }

        /// <summary>
        /// Gets or sets the donor's zip code
        /// </summary>
        public string ZipCode
        {
            get
            {
                return this.zipCode;
            }

            set
            {
                if (this.zipCode != value)
                {
                    this.zipCode = value;

                    this.Offset = 0;
                }
            }
        }

        /// <summary>
        /// Gets or sets the reporting year
        /// </summary>
        public int Year
        {
            get
            {
                return this.year;
            }

            set
            {
                if (this.year != value)
                {
                    if (value < 2000)
                    {
                        throw new ArgumentOutOfRangeException("value", "2000 and later for congressional campaigns, 2008 and later for presidential campaigns.");
                    }

                    this.year = value;

                    this.Offset = 0;
                }
            }
        }

        /// <summary>
        /// Gets or sets the donor's first name
        /// </summary>
        public string FirstName
        {
            get
            {
                return this.firstName;
            }

            set
            {
                if (this.firstName != value)
                {
                    this.firstName = value;

                    this.Offset = 0;
                }
            }
        }

        /// <summary>
        /// Gets or sets the maximum number of results
        /// </summary>
        public int MaxResults
        {
            get
            {
                return this.maxResults;
            }

            set
            {
                if (this.maxResults != value)
                {
                    this.maxResults = value;

                    this.Offset = 0;
                }
            }
        }

        /// <summary>
        /// Gets or sets the offset (in increments of 100)
        /// </summary>
        public int Offset
        {
            get
            {
                return this.offset;
            }

            set
            {
                this.offset = value;

                FetchData();
            }
        }

        /// <summary>
        /// Gets the donor results
        /// </summary>
        public ObservableCollection<Donor> Results { get; private set; }

        /// <summary>
        /// Gets a value indicating whether the donor information can be retrieved
        /// </summary>
        protected override bool CanFetchData
        {
            get
            {
                if (string.IsNullOrEmpty(this.LastName) && string.IsNullOrEmpty(this.ZipCode))
                {
                    return false;
                }

                return true;
            }
        }

        /// <summary>
        /// If the offset is 0, clear the results
        /// </summary>
        protected override void ClearResults()
        {
            if (this.Offset == 0)
            {
                this.Results.Clear();
            }
        }

        /// <summary>
        /// Generate an address for the service
        /// </summary>
        /// <param name="baseAddress">the base address</param>
        /// <returns>the service URI</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Not relevant for URI")]
        protected override Uri GenerateAddress(string baseAddress)
        {
            var builder = new StringBuilder(baseAddress);

            if (string.IsNullOrEmpty(this.LastName))
            {
                builder.AppendFormat(
                    CultureInfo.InvariantCulture,
                    "{0}/{1}/contributions/donorsearch.xml?zip={2}",
                    this.Year,
                    this.CampaignType.ToString().ToLowerInvariant(),
                    this.ZipCode);
            }
            else
            {
                builder.AppendFormat(
                    CultureInfo.InvariantCulture,
                    "{0}/president/contributions/donorsearch.xml?lname={1}",
                    this.Year,
                    this.LastName);
            }

            if (!string.IsNullOrEmpty(this.FirstName))
            {
                builder.AppendFormat(
                    CultureInfo.InvariantCulture,
                    "&fname={0}",
                    this.FirstName);
            }

            if (this.Offset > 0)
            {
                builder.AppendFormat(
                    CultureInfo.InvariantCulture,
                    "&offset={0}",
                    this.Offset);
            }

            builder.AppendFormat(
                CultureInfo.InvariantCulture,
                "&api-key={0}",
                this.ApiKey);

            return new Uri(builder.ToString(), UriKind.Absolute);
        }

        /// <summary>
        /// Fill the result list
        /// </summary>
        /// <param name="results">the results element</param>
        /// <returns>false if there are more results, true otherwise</returns>
        protected override bool ProcessResults(System.Xml.Linq.XElement results)
        {
            if (results == null)
            {
                throw new ArgumentNullException("results");
            }

            var donors = from donor in results.Elements("donor")
                         select new Donor()
                         {
                             Address1 = donor.Element("donor_address1").Value,
                             Address2 = donor.Element("donor_address2").Value,
                             AggregateDonationAmount = string.IsNullOrEmpty(donor.Element("aggregate_donation_amount").Value) ? 0 : double.Parse(donor.Element("aggregate_donation_amount").Value, CultureInfo.InvariantCulture),
                             CandidateFullName = donor.Element("candidate_full_name").Value,
                             City = donor.Element("donor_city").Value,
                             DonationAmount = double.Parse(donor.Element("donation_amount").Value, CultureInfo.InvariantCulture),
                             DonationDate = DateTime.Parse(donor.Element("donation_date").Value, CultureInfo.InvariantCulture),
                             FirstName = donor.Element("donor_first_name").Value,
                             LastName = donor.Element("donor_last_name").Value,
                             MiddleName = donor.Element("donor_middle_name").Value,
                             Prefix = donor.Element("donor_prefix").Value,
                             State = donor.Element("donor_state").Value,
                             Suffix = donor.Element("donor_suffix").Value,
                             Zip = donor.Element("donor_zip5").Value
                         };

            foreach (var donor in donors)
            {
                if (this.Results.Count >= this.MaxResults)
                {
                    return true;
                }

                this.Results.Add(donor);
            }

            if (donors.Count() >= 100)
            {
                this.Offset += 100;

                return false;
            }

            return true;
        }
    }
}
